package cn.touna.jss.common.aspect;

import cn.touna.jss.common.aspect.entity.CacheCreditQueryRequest;
import cn.touna.jss.common.aspect.entity.CacheCreditResponseHelper;
import cn.touna.jss.common.cache.HbaseComponent;
import cn.touna.jss.common.dict.CreditIntfConfCache;
import cn.touna.jss.common.dict.entity.IntfConfEntity;
import cn.touna.jss.common.dict.enums.IntfStatusEnum;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.properties.config.SysConfigProperties;
import cn.touna.jss.common.util.Common;
import cn.touna.jss.common.util.DateUtils;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.adapter.ApiUtils;
import cn.touna.risk.api.domain.Response;
import cn.touna.risk.api.entity.RequestEntity;
import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.risk.api.exception.BusinessException;
import cn.touna.risk.api.exception.ServiceException;
import cn.touna.risk.api.lock.RedisLockService;
import cn.touna.risk.api.utils.IntfTypeUtil;
import cn.touna.risk.api.utils.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import com.touna.loan.log.service.TracerContextUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;

/**
 * \* User: MeiZhongHao
 * \* Date: 2018-06-12
 * \* Time: 19:14
 * \* Description:
 * \
 */
@Component
public class ApiMappingCacheService {

    private static final Logger logger = LogManager.getLogger(ApiMappingCacheService.class);
    @Autowired
    protected HbaseComponent hbaseComponent;
    @Autowired
    private CreditIntfConfCache creditIntfConfCache;
    @Autowired
    private RedisLockService redisLockService;

    @Autowired
    SysConfigProperties sysConfigProperties;

    /****
     * 根据 useCache变量与配置的对应缓存策略进行缓存的存储
     * @param pjp
     * @param apiMapping
     * @return
     * @throws Throwable
     */
    public Object audit(ProceedingJoinPoint pjp, ApiMapping apiMapping) throws Throwable {
        Response response = new Response();
        String apiUrl = null;
        String lockRowkey = null;
        boolean lockBoolean = false;
        try {
            Object[] args = pjp.getArgs();
            apiUrl = apiMapping.value();
            CacheCreditQueryRequest request = new CacheCreditQueryRequest();
            // 若开启缓存，则去查询是否有缓存配置，若有相关缓存配置，则读取缓存，若缓存结果为正确结果，则返回
            if (apiMapping.useCache()) {
                request.setParams(JSONUtil.getJSONObject(JSON.toJSONString(args[0])));
                // 获取对应缓存策略数据
                IntfConfEntity intfConf = null;
                try {
                    intfConf = creditIntfConfCache.getExistInfConf(apiUrl);
                } catch (Exception e) {
                    logger.warn("获取缓存策略信息异常", e);
                }
                // 组装数据
                request = buildQueryCacheParams(apiUrl, request, intfConf);
                //暂时注释
                //暂时注释不做产品验证
                if (intfConf != null && sysConfigProperties.getCheckStatus().equals("Y")) {
                    IntfStatusEnum statusEnum = IntfStatusEnum.getloanStatus(intfConf.getStatus());
                    if (IntfStatusEnum.VALID.value() != statusEnum.value()) {
                        return ResponseFactory.fail(SystemErrorCodeEnum.JSS_PARAM_CHECK_FAILED.getErrorCode(), "接口已" + statusEnum.text());
                    }
                }

                //缓存开关为Y，则查询缓存
                if (request.getCacheQueryAbleBoolValue()) {
                    lockRowkey = hbaseComponent.getRowKeyByStrategy(request);
                    if (StringUtils.isNotBlank(lockRowkey)) {
                        lockBoolean = redisLockService.lock(lockRowkey);
                        if (!lockBoolean) {
                            logger.info("拦截并发请求:{}", JSONObject.toJSON(request));
                            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SERVICE_CONCURRENT_ERROR);
                        }
                    }
                    // 查询缓存数据
                    response = queryDataFromCache(request);
                    // 判断缓存数据是否为成功请求数据
                    if (response.checkIfSuccess()) {
                        String cacheKey = hbaseComponent.getRowKeyByStrategy(request);
                        response.setSrc(1);
                        logger.info(String.format("traceId:%s response from cache,cacheKey:%s", response.getFlowNo(), cacheKey));
                        return response;
                    }
                }
            }
            Object result = pjp.proceed();
            response = ApiUtils.hanldReturnResult(result);

            if (apiMapping.useCache() && request.getCacheQueryAbleBoolValue() && response.checkIfSuccess()) {
                hbaseComponent.saveCreditResponseToCache(request, response);
            }
            return response;
        } catch (BusinessException e) {
            response = ApiUtils.handError(e, apiUrl);
            return response;
        } catch (ServiceException e) {
            response = ApiUtils.handError(e, apiUrl);
            return response;
        } catch (InvocationTargetException e) {
            Throwable t = e.getTargetException();
            response = ApiUtils.handError(t, apiUrl);
            return response;
        } catch (Exception e) {
            response = ApiUtils.handError(e, apiUrl);
            return response;
        } finally {
            if (lockBoolean && StringUtils.isNotBlank(lockRowkey)) {
                this.redisLockService.unLock(lockRowkey);
            }
        }
    }


    /**
     * 查询数据库接口配置
     *
     * @param requestEntity
     * @return
     * @MeiZhongHao
     */
    private CacheCreditQueryRequest buildQueryCacheParams(String apiMethod, RequestEntity requestEntity, IntfConfEntity intfConf) {
        CacheCreditQueryRequest params = new CacheCreditQueryRequest();
        try {
            params.setCrNo(TracerContextUtil.getTraceId());
            params.setBizType(requestEntity.getBizType());
            params.setQueryTime(new Date());
            params.setServiceName(IntfTypeUtil.splitIntfType(apiMethod)[0]);
            params.setMethodName(IntfTypeUtil.splitIntfType(apiMethod)[1]);
            params.setMethod(apiMethod);
            params.setParams(requestEntity.getParams());
            params.setIdCard(JSONUtil.getString(requestEntity.getParams(), "idCard", null));
            params.setMobile(JSONUtil.getString(requestEntity.getParams(), "mobile", null));
            params.setClientIp(requestEntity.getClientIp());
//            params.setStatus(intfConf.getStatus());
            if (intfConf != null) {
                params.setCacheQueryAble(intfConf.getCacheAble());
                params.setExpiryDate(intfConf.getExpiryDate());
                params.setCacheStrategy(intfConf.getCacheStrategy());
            }
        } catch (Exception e) {
            logger.warn("【接口配置初始化】异常，msg:{}", e.getMessage());
        }
        return params;
    }

    /**
     * @MeiZhongHao TODO
     * 查询有效缓存数据
     */
    private Response queryDataFromCache(CacheCreditQueryRequest request) {
        Response response = new Response();
        try {
            String result = hbaseComponent.queryCreditResponseFromCache(request);
            if (StringUtils.isNotBlank(result)) {
                Long start = JSONUtil.getLong(result, CacheCreditResponseHelper.timestamp, null);
                // 判断缓存策略定义的缓存有效时间，若超时则不使用该缓存（-1则为永久有效）
                JSONObject resultObj = JSONObject.parseObject(result);
                if (request.getExpiryDate() == -1 || DateUtils.isValidDate(start, System.currentTimeMillis(), request.getExpiryDate())) {
                    Object data = resultObj.get(CacheCreditResponseHelper.data);//JSONUtil.get(result, CacheCreditResponseHelper.data, null);
                    String dataClass = "";
                    try {
                        dataClass = resultObj.getString(CacheCreditResponseHelper.dataClass);
                        if (!StringUtils.isEmpty(dataClass) && !"null".equals(dataClass)) {
                            if ("com.alibaba.fastjson.JSONObject".equals(dataClass)) {
                                data = JSONObject.parseObject(String.valueOf(data));
                            } else if ("com.alibaba.fastjson.JSONArray".equals(dataClass)) {
                                data = JSONArray.parseArray(String.valueOf(data));
                            } else if ("java.lang.String".equals(dataClass)) {
                                data = String.valueOf(data);
                            } else if ("java.utils.HashMap".equals(dataClass)) {
                                //如果类型是HashMap,尝试将HashMap格式化字符串 反序列化为JSONObject
                                data = Common.mapStr2Json(String.valueOf(data));
                            } else {
                                Class clazz = Class.forName(dataClass);
                                if (!isJavaClass(clazz)) {
                                    data = JSON.parseObject((String) data, clazz);
                                } else {
                                    data = TypeUtils.cast(data, clazz, ParserConfig.getGlobalInstance());
                                }
                            }
                        }
                    } catch (Exception e) {
                        logger.error("缓存的data转换成" + dataClass + "失败," + e.getMessage() + ",data:" + data, e);
                    }

                    if (null != data) {
                        response.success(SystemErrorCodeEnum.SYSTEM_REQUEST_SUCC, data);
                    }
                } else {
                    throw new BusinessException(SystemErrorCodeEnum.CRS_CACHE_FAILED);
                }
            } else {
                throw new BusinessException(SystemErrorCodeEnum.CRS_CACHE_FAILED);
            }
        } catch (BusinessException e) {
            logger.warn("【查询缓存错误】,method：{},msg:{}", request.getMethod(), e.getMessage());
            response.fail(SystemErrorCodeEnum.CRS_CACHE_FAILED);
        } catch (Exception e) {
            logger.warn("【查询缓存错误】,method：{},msg:{}", request.getMethod(), e.getMessage());
            response.fail(SystemErrorCodeEnum.CRS_CACHE_FAILED);
        }
        response.setSrc(1);
        return response;
    }

    private static boolean isJavaClass(Class<?> clz) {
        return clz != null && clz.getClassLoader() == null;
    }
}